﻿//----------------------------------------------------------------------------
// Copyright (c) Pavel Tishkin 2012-2013.
//
// You must not remove this notice, or any other, from this software.
//----------------------------------------------------------------------------

namespace Lib.Dxp.Core


///<summary>Text processing module by replacing text parts.
/// </summary>
[<AutoOpenAttribute>]
module Text = 

    ///<summary>Convert `elem` to string and replaces \n -> "<br/>" | \r -> ""
    /// <para><c> := nl2br  elem </c></para> </summary>
    ///<param name="elem">Any object</param>
    // ------------------------------------------------------------------------------------------
    let inline nl2br     (elem : 'a)   = ( elem |> to_s ).Replace("\n", "<br/>").Replace("\r", "")


    ///<summary>Convert `elem` to string and replaces \n -> " " | \r -> ""
    /// <para><c> := no_nl  elem </c></para> </summary>
    ///<param name="elem">Any object</param>
    // ------------------------------------------------------------------------------------------
    let inline no_nl     (elem : 'a)   = ( elem |> to_s ).Replace("\n", " ").Replace("\r", "")


    ///<summary>Convert `elem` to string and replaces \t to " "
    /// <para><c> := no_tabs  elem </c></para> </summary>
    ///<param name="elem">Any object</param>
    // ------------------------------------------------------------------------------------------
    let inline no_tabs   (elem : 'a)   = ( elem |> to_s ).Replace("\t", " ")


    ///<summary>Convert `elem` to string and replaces " and ' to ` 
    /// <para><c> := no_quotes  elem </c></para> </summary>
    ///<param name="elem">Any object</param>
    // ------------------------------------------------------------------------------------------
    let inline no_quotes (elem : 'a)   = ( elem |> to_s ).Replace("'", "`").Replace('"', '`')


    ///<summary>Splits `text` string by `delim` char. Returns string [] or [||] for `text` == null or "".
    /// <para><c> := splitBy  delim  text </c></para> </summary>
    ///<param name="delim">Delimiter</param>
    ///<param name="text">Divided string</param>
    ///<returns></returns>
    // ------------------------------------------------------------------------------------------
    let splitBy (delim : char) (text : string)  = 
        match text with null | "" -> [||] | _     -> text.Split(delim)


    ///<summary>Splits `text` string by `delim` regular expression. Returns string [] or [||] for `text` == null or "".
    /// <para><c> := splitRe  delim  text </c></para> </summary>
    ///<param name="delim">Delimiter</param>
    ///<param name="text">Divided string</param>
    ///<returns></returns>
    // ------------------------------------------------------------------------------------------
    let splitRe (delim : string) (text : string)  = 
        match delim with 
        | null | ""    -> [||] 
        | _  -> match text with 
                | null 
                | ""    -> [||] 
                | _     -> let rgx = System.Text.RegularExpressions.Regex(delim, System.Text.RegularExpressions.RegexOptions.None)
                           rgx.Split text


    ///<summary>Trims `text` . Returns "" for `text` == null.
    /// <para><c> := trim  text </c></para> </summary>
    ///<param name="text">Original string</param>
    ///<returns></returns>
    // ------------------------------------------------------------------------------------------
    let trim (text : string)            = match text with null | "" -> "" | _     -> text.Trim()


    ///<summary>Convert `elem` to string and replaces '&' -> '&amp;' | '<' -> '&lt;' | '>' -> '&gt;' 
    /// <para><c> := no_ltgtamp  elem </c></para> </summary>
    ///<param name="elem">Any object</param>
    // ------------------------------------------------------------------------------------------
    let inline no_ltgtamp  (elem : 'a)   = ( elem|> to_s ).Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
    let safeentity x = no_ltgtamp x


    ///<summary>Convert `elem` to string and apply `no_ltgtamp >> nl2br >> no_quotes`
    /// <para><c> := escape  elem </c></para> </summary>
    ///<param name="elem">Any object</param>
    // ------------------------------------------------------------------------------------------
    let inline escape  (elem : 'a)   =  elem |> no_ltgtamp |> nl2br |> no_quotes


    let pad_left n (text : string) = 
        let l = text.Length
        if l >= n then text else String.replicate (n - l) " " + text
    
    let pad_right n (text : string) = 
        let l = text.Length
        if l >= n then text else text + String.replicate (n - l) " "



module Fmt = 
    /// Format constants & `Sprintf`, `Sformat` modules
    module String = 
        // --------------------------------------------------------------------------------------
        /// "yyyy-MM-dd"
        let [<Literal>] DATETIME_ISO    = "yyyy-MM-dd HH:mm:ss"
        /// "yyyy-MM-dd"
        let [<Literal>] DATE_ISO        = "yyyy-MM-dd"
        /// "yyyyMMddhhmmss"
        let [<Literal>] DATETIMESOLID   = "yyyyMMddHHmmss"
        /// "HH:mm:ss"
        let [<Literal>] TIME24_ISO      = "HH:mm:ss"
        /// {0}="{1}"
        let [<Literal>] HTML_ATTR       = "{0}=\"{1}\""



    /// Functions for format strings in F# way ----------------------------------------------
    module Sprintf =
        let DATETIME_ISO            = sprintf "%s-%s-%s %s:%s:%s"
        let TIME_MISS               = sprintf "%02i:%02i"
        let TIME_ISO                = sprintf "%i:%02i:%02i"
        let F2                      = sprintf "%.2f"                // sprintf "%15.2f" (1.0/3.0)  == right padded 
        let F4                      = sprintf "%.4f"                



    /// Functions for format strings in C# way ----------------------------------------------
    module Sformat = 
        let CLOSED_TAG (e : string)     = System.String.Format("<{0} />\r\n", e)
        let STARTOF_TAG (e : string)    = System.String.Format("<{0}>", e)
        let ENDOF_TAG (e : string)      = System.String.Format("</{0}>\r\n", e)
        //let private _FullTag     = "<{0}>{1}</{2}>"

        let ERR_Unsupported_Operation   (x : obj)           = System.String.Format("Действие `{0}` не определено", x)
        let ERR_Unsupported_OperationIn (x : obj) (y : obj) = System.String.Format("Unsupported command `{0}` for `{1}", x, y)
        let ERR_AbsentFormParameter     (x : obj)           = System.String.Format("Отсуствует значение формы `{0}`", x)

        




module Sql =

    module Text =

        /// "(DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0))"
        let [<Literal>] CURRENT_DATE            = "(DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0))"
        /// "(GETDATE())"
        let [<Literal>] CURRENT_DATETIME        = "(GETDATE())"

        let [<Literal>] And             = " AND "
        let [<Literal>] Or              = " OR "
        let [<Literal>] CommaNl         = ",\r\n       "
        let [<Literal>] Comma           = ", "
        let NlAnd                       = "\r\n  " + And

        module Prefix =
            let [<Literal>] ArchiveTable        = "arch_"
    
        module SpecialField =
            let [<Literal>] ArchiveTable_ID     = "AID"
            let [<Literal>] Archive_MovedDt     = "DeletedDt"
            let [<Literal>] Archive_MovedBy     = "DeletedBy"

        module Part = 
            let [<Literal>] Desc                = " DESC"



    type SqlPartString(text : string) =
        static member CurrentDate       = SqlPartString(Text.CURRENT_DATE)
        static member CurrentDateTime   = SqlPartString(Text.CURRENT_DATETIME)

        override x.ToString() = text



    module Sformat = 
        ///  tblname fldname -> format "{tblname}.{fldname}"
        let tableFieldName tblname fldname  = "{0}.{1}".format(tblname, fldname)
        /// "{0}__{1}".format(tblname, fldname)
        let htmlIdFor tblname fldname       = "{0}__{1}".format(tblname, fldname)

        /// SQL extract only date from datetime 
        let DateTime2Date(fld : string)     = "(DATEADD(day, DATEDIFF(day, 0, {0}), 0))".format(fld)

        /// "{0} AS '{1}'".format(fldname, title)
        let asTitle (fldname : string) (title : string)           = "{0} AS '{1}'".format(fldname, title)
        let asSynonym (fldname : string) (title : string)           = "{0} AS {1}".format(fldname, title)
        /// "[{0}] AS '{1}'".format(fldname, title)
        let brackedAsTitle (fldname : string) (title : string)    = "[{0}] AS '{1}'".format(fldname, title)
        /// "{0} = @a{1}".format(fldname, no)
        let fieldEqParam fldname no         = "{0} = @a{1}".format(fldname, no)

        /// "\r\n        {0} {1} {2} ".format(t1, jointype, t2)
        let joinOn t1 jointype t2           = "\r\n        {0} {1} {2} ".format(t1, jointype, t2)

        /// "{0}\r\n ORDER BY {1}".format(qry, ordtext)
        let ExtendWithOrderBy qry ordtext   = "{0}\r\n ORDER BY {1}".format(qry, ordtext)
        /// "{0}\r\n GROUP BY {1}".format(qry, grptext)
        let ExtendWithGroupBy qry grptext   = "{0}\r\n GROUP BY {1}".format(qry, grptext)
        /// "{0}\r\n WHERE ( {1} )".format(qry, whrtext)
        let ExtendWithWhere qry whrtext     = "{0}\r\n WHERE ( {1} )".format(qry, whrtext)

        /// "\r\n ORDER BY {0}".format(ordtext)
        let OrderBy ordtext                 = ExtendWithOrderBy "" ordtext
        /// "\r\n GROUP BY {0}".format(grptext)
        let GroupBy grptext                 = ExtendWithGroupBy "" grptext
        /// "\r\n WHERE ( {0} )".format(whrtext)
        let Where whrtext                   = ExtendWithWhere "" whrtext
        /// "@a{0}".format(n)
        let paramNo (n : int)               = "@a{0}".format(n)

        /// SELECT {0}\r\n  FROM {1}
        let SelectFrom (flds : string) (tbls : string)                   = 
            if isSome tbls then "SELECT {0}\r\n  FROM {1} ".format(flds, tbls)
                           else "SELECT {0} ".format(flds)
        /// INSERT INTO {0}({1}{2}) VALUES ({3}{4})
        let Insert tbl flds extflds vals extvals    = "INSERT INTO {0}({1}{2})\r\n VALUES ({3}{4})".format(tbl, flds, extflds, vals, extvals)
        /// UPDATE {0} SET {1}
        let Update tbl setvals                      = "UPDATE {0} SET {1} ".format(tbl, setvals)
        /// UPDATE {0} SET {1}{2} WHERE ID = @a1
        let UpdateWhere tbl setvals extsetvals      = "UPDATE {0} SET {1}{2} WHERE ID = @a1".format(tbl, setvals, extsetvals)
        /// DELETE FROM {0}
        let Delete (tbl : obj)                      = "DELETE FROM {0} ".format(tbl)
        /// DELETE FROM {0} WHERE ID = @a1 {1}
        let DeleteWhere tbl extwhere                = "DELETE FROM {0} WHERE ID = @a1 {1}".format(tbl, extwhere)


        /// Wrap `entire_sql` with [`from_limit`..`till_limit`] bounds ordered by 'orderby'
        let wrapFromTo(entire_sql : string, from_limit, till_limit, orderby : string) =
            let regex = new System.Text.RegularExpressions.Regex(@"FROM\s")
            let paged_qry = 
                "SELECT * FROM ({0}) _ WHERE [_RowN] > {1} AND [_RowN] <= {2}".format(
                            (regex.Replace(entire_sql, ", (row_number() OVER (ORDER BY {0})) AS [_RowN] FROM ".format(orderby), 1))
                            , from_limit
                            , till_limit
                            )
            paged_qry
        

    

    
